package com.yihaodian.search.core;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.search.FieldDoc;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.TopDocsCollector;

import com.yihaodian.api.search.SearchResponse;
import com.yihaodian.api.search.misc.SearchHit;
import com.yihaodian.search.SearchContext;
import com.yihaodian.search.query.FilterBuilder;
import com.yihaodian.search.query.QueryBuilder;
import com.yihaodian.search.query.SortBuilder;

public class SearchHandler {

	static Logger log = Logger.getLogger(SearchHandler.class);

	QueryBuilder queryBuilder;
	FilterBuilder filterBuilder;
	SortBuilder sortBuilder;

	public SearchHandler() {
		this.queryBuilder = new QueryBuilder();
		this.sortBuilder = new SortBuilder();
		this.filterBuilder = new FilterBuilder();
	}

	public void handle(SearchContext context) {
		preProcess(context);

		log.debug(context);
		
		Query query = context.getQuery();
		Filter filter = context.getFilter();
		int topN = context.getRequest().getOffset()
				+ context.getRequest().getCount();
		Sort sort = context.getSort();
	
		TopDocs topDocs = null;

		TopDocsCollector collecotr = context.getCollector();
		try {
			IndexSearcher indexSearcher = context.getSearcher().searcher();
			if(sort!=null){
				sort=sort.rewrite(indexSearcher);
			}
			if (collecotr == null) {
				topDocs = indexSearcher.search(query, filter, topN, sort, true,
						false);
			} else {
				indexSearcher.search(query, filter, collecotr);
				topDocs = collecotr.topDocs();
			}

		} catch (IOException e) {
			log.error(e.getMessage(), e);
		}

		postProcess(context, topDocs);
	}

	public void preProcess(SearchContext context) {
		filterBuilder.handle(context);
		queryBuilder.handle(context);
		sortBuilder.handle(context);
	}

	public void postProcess(SearchContext context, TopDocs topDocs) {
		SearchResponse response = context.getResponse();

		IndexReader reader = context.getSearcher().reader();

		ScoreDoc[] scoreDocs = topDocs.scoreDocs;
		response.setTotalHit(topDocs.totalHits);
		SearchHit[] hits = new SearchHit[scoreDocs.length];
		response.setHits(hits);
		for (int i = 0; i < scoreDocs.length; i++) {
			SearchHit hit = new SearchHit();
			hits[i] = hit;
			FieldDoc fieldDoc = (FieldDoc) scoreDocs[i];
			hit.setDocId(fieldDoc.doc);
			hit.setScore(fieldDoc.score);
			hit.setSortValues(fieldDoc.fields);
			try {
				Map<String, String> fieldValues = new HashMap<String, String>();
				Document doc = reader.document(fieldDoc.doc);
				Set<String> fields = context.getRequest().getFields();
				if (fields != null && fields.size() > 0) {
					for (String field : fields) {
						fieldValues.put(field, doc.get(field));
					}
				} else {
					List<IndexableField> storeFields = doc.getFields();
					for (IndexableField f : storeFields) {
						fieldValues.put(f.name(), f.stringValue());
					}
				}
				hit.setFieldValues(fieldValues);
			} catch (Exception e) {
				log.error(e.getMessage(), e);
			}
		}
	}
}
